﻿using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

namespace DataCenter.Share
{
    [Serializable]
    public class CacheStream : Stream, ICloneable
    {
        public const int BYTES_PER_MB = 1048576;

        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern IntPtr CreateFile(string lpFileName,
                                               uint dwDesiredAccess,
                                               uint dwShareMode,
                                               IntPtr lpSecurityAttributes,
                                               uint dwCreationDisposition,
                                               uint dwFlagsAndAttributes,
                                               IntPtr hTemplateFile);

        public CacheStream(int capacity)
        {
            this.Capacity = capacity;
            this._Stream = new MemoryStream(capacity);
        }
        public CacheStream() : this(BYTES_PER_MB) { }

        private long Capacity;
        private bool IsInCacheMode = false;
        private Stream _Stream;

        public override bool CanRead
        {
            get { return true; }
        }

        public override bool CanSeek
        {
            get { return true; }
        }

        public override bool CanWrite
        {
            get { return true; }
        }

        public override void Flush()
        {
            _Stream.Flush();
        }

        public override long Length
        {
            get { return _Stream.Length; }
        }

        public override long Position
        {
            get { return _Stream.Position; }
            set { _Stream.Position = value; }
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            return _Stream.Read(buffer, offset, count);
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            return _Stream.Seek(offset, origin);
        }

        public override void SetLength(long value)
        {
            ChangeToFileStream(value);
            SetLength(value);
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            ChangeToFileStream(buffer.Length);
            _Stream.Write(buffer, offset, count);
        }

        public override void WriteByte(byte value)
        {
            ChangeToFileStream(sizeof(byte));
            _Stream.WriteByte(value);
        }

        public override void Close()
        {
            if (_Stream != null)
            {
                _Stream.Close();
            }

            base.Close();
        }

        private void ChangeToFileStream(long bufferLength)
        {
            if (!IsInCacheMode)
            {
                if (_Stream.Length + bufferLength > Capacity)
                {
                    SafeFileHandle pFile = CreateTempFile();
                    FileStream fs = new FileStream(pFile, FileAccess.ReadWrite);
                    byte[] buffer = ((MemoryStream)_Stream).GetBuffer();
                    fs.Write(buffer, 0, buffer.Length);
                    fs.Position = _Stream.Position;
                    _Stream.Close();
                    _Stream = fs;                    
                    this.IsInCacheMode = true;
                }
            }
        }

        private SafeFileHandle CreateTempFile()
        {
            string tempFile = Path.GetTempFileName();
            IntPtr pFile = CreateFile(tempFile, 268435456, 3, IntPtr.Zero, 2, 67108864, IntPtr.Zero);
            SafeFileHandle fileHandle = new SafeFileHandle(pFile, true);
            if (pFile.ToInt32() == -1)
                throw new Exception("创建文件" + tempFile + "失败");
            return fileHandle;
        }

        #region ICloneable Members

        public object Clone()
        {
            return Utils.Clone(this);
        }

        #endregion
    }
}
